#include <stdio.h>
#include <stdlib.h>
#include <ustr.h>
#include "lib/confdata.h"
#include "lib/target.h"
#include "lib/util.h"

static int check_args (const char  *testname,
                             char  *subtest, /* a pointer to a single char */
                       int          EXPECTED_ARGC,
                       const char **EXPECTED_ARGV,
                       int          argc,
                       char       **argv)
{
  char **scan = NULL;
  int    i = 0;

  if (argc != EXPECTED_ARGC)
    {
      errorMsg ("FAILED %s%c: argc = %d, expected %d", testname, *subtest,
                argc, EXPECTED_ARGC);
      return 1;
    }
  (*subtest)++;
  if (argv == NULL)
    {
      errorMsg ("FAILED %s%c: argv is NULL", testname, *subtest);
      return 1;
    }
  (*subtest)++;
  /* explicitly count entries, stopping at first NULL */
  for (i = 0, scan = argv; *scan != NULL; i++, scan++)
    ;
  if (i != EXPECTED_ARGC)
    {
      errorMsg ("FAILED %s%c: argv does not contain %d entries",
                testname, *subtest, EXPECTED_ARGC);
      return 1;
    }
  (*subtest)++;

  /* now, compare each entry */
  for (i = 0, scan = argv; i < EXPECTED_ARGC && *scan != NULL; i++, scan++)
    {
      if (strcmp (EXPECTED_ARGV[i], *scan) != 0)
        {
          errorMsg ("FAILED %s%c: argv[%d] = |%s|, expected |%s|",
                    testname, *subtest, i, *scan, EXPECTED_ARGV[i]);
          return 1;
        }
    }
  (*subtest)++;
  return 0;
}

int test1 (void)
{
  int rc = 1;
  run2_selfoptions_t * selfopt;
  size_t EXPECTED_ARGC = 4;
  const char *EXPECTED_ARGV[] = {
    "--debug",
    "2",
    "\"\"",
    "--wait",
    NULL
  };
  const char *testname = "test 1";
  char subtest = 'a';
  int    argc = 4;
  char **argv = NULL;

  selfopt = run2_selfoptions_create ();
  run2_selfoptions_add_arg (selfopt, USTR1 (\7, "--debug"));
  run2_selfoptions_add_arg (selfopt, USTR1 (\1, "2"));
  run2_selfoptions_add_arg (selfopt, USTR1 (\0, ""));     /* doesn't get added */
  run2_selfoptions_add_arg (selfopt, USTR1 (\2, "\"\"")); /* does get added */
  run2_selfoptions_add_arg (selfopt, USTR1 (\6, "--wait"));

  run2_selfoptions_print (stdout, 0, USTR1 (\xF, "test1 (selfopt)"), selfopt);

  if (selfopt->args == NULL)
    {
      errorMsg ("FAILED %s%c: selfopt->args is NULL", testname, subtest);
      goto cleanup;
    }
  subtest++;

  if (cdsl_dlist_size (selfopt->args) != EXPECTED_ARGC)
    {
      errorMsg ("FAILED %s%c: length(selfopt->args) = %d, expected %d",
                testname, subtest, cdsl_dlist_size (selfopt->args),
                EXPECTED_ARGC);
      goto cleanup;
    }
  subtest++;

  run2_create_argv_from_dlist (selfopt->args, NULL, &argc, &argv);
  if (check_args (testname, &subtest, EXPECTED_ARGC, EXPECTED_ARGV, argc, argv) != 0)
    goto cleanup;

  rc = 0;

cleanup:
  run2_selfoptions_delete (selfopt);
  run2_freeargv (argv);
  return rc;
}

int test2 (void)
{
  int rc = 1;
  run2_tgt_spec_t *tgtspec;
  size_t EXPECTED_ARGC = 3;
  const char *EXPECTED_ARGV[] = {
    "a",
    "b",
    "c",
    NULL
  };
  const char *testname = "test 2";
  char subtest = 'a';
  int    argc = 3;
  char **argv = NULL;

  tgtspec = run2_tgt_spec_create ();
  run2_tgt_spec_add_arg (tgtspec, USTR1 (\1, "a"));
  run2_tgt_spec_add_arg (tgtspec, USTR1 (\1, "b"));
  run2_tgt_spec_add_arg (tgtspec, USTR1 (\1, "c"));
  run2_tgt_spec_set_filename (tgtspec, USTR1 (\x11, "/usr/bin/test.exe"));
  run2_tgt_spec_set_startin (tgtspec, USTR1 (\x8, "/usr/bin"));

  run2_tgt_spec_print (stdout, 0, USTR1 (\xF, "test2 (tgtspec)"), tgtspec);

  if (tgtspec->args == NULL)
    {
      errorMsg ("FAILED %s%c: tgtspec->args is NULL", testname, subtest);
      goto cleanup;
    }
  subtest++;

  if (cdsl_dlist_size (tgtspec->args) != EXPECTED_ARGC)
    {
      errorMsg ("FAILED %s%c: length(tgtspec->args) = %d, expected %d",
                testname, subtest, cdsl_dlist_size (tgtspec->args),
                EXPECTED_ARGC);
      goto cleanup;
    }
  subtest++;

  run2_create_argv_from_dlist (tgtspec->args, NULL, &argc, &argv);
  if (check_args (testname, &subtest, EXPECTED_ARGC, EXPECTED_ARGV, argc, argv) != 0)
    goto cleanup;

  rc = 0;

cleanup:
  run2_tgt_spec_delete (tgtspec);
  run2_freeargv (argv);
  return rc;
}

int test3 (void)
{
  int rc = 1;
  run2_tgt_spec_t *tgtspec;
  size_t EXPECTED_ARGC = 5;
  const Ustr *ARGV_USTR[] = {
    USTR1 (\6, "MARKER"),
    USTR1 (\xB, "with spaces"),
    USTR1 (\x17, "and\\backslash'withquote"),
    USTR1 (\2, "--"),
    USTR1 (\x8, "last arg")
  };
  const char *EXPECTED_ARGV[] = {
    ustr_cstr (ARGV_USTR[0]),
    ustr_cstr (ARGV_USTR[1]),
    ustr_cstr (ARGV_USTR[2]),
    ustr_cstr (ARGV_USTR[3]),
    ustr_cstr (ARGV_USTR[4]),
    NULL
  };
  const Ustr *FILENAME_USTR = USTR1 (\x11, "/usr/bin/test.exe");

  const char *EXPECTED_CMDLINE =
    "MARKER \"with spaces\" and\\backslash'withquote -- \"last arg\"";
  const char *testname = "test 3";
  char subtest = 'a';
  int    argc = 4;
  char **argv = NULL;
  char  *cmd = NULL;
  char  *p = NULL;

  tgtspec = run2_tgt_spec_create ();
  run2_tgt_spec_add_arg (tgtspec, ARGV_USTR[0]);
  run2_tgt_spec_add_arg (tgtspec, ARGV_USTR[1]);
  run2_tgt_spec_add_arg (tgtspec, ARGV_USTR[2]);
  run2_tgt_spec_add_arg (tgtspec, ARGV_USTR[3]);
  run2_tgt_spec_add_arg (tgtspec, ARGV_USTR[4]);
  run2_tgt_spec_set_filename (tgtspec, FILENAME_USTR);
  run2_tgt_spec_set_startin (tgtspec, USTR1 (\x8, "/usr/bin"));

  run2_tgt_spec_print (stdout, 0, USTR1 (\xF, "test3 (tgtspec)"), tgtspec);

  if (tgtspec->args == NULL)
    {
      errorMsg ("FAILED %s%c: tgtspec->args is NULL", testname, subtest);
      goto cleanup;
    }
  subtest++;

  if (cdsl_dlist_size (tgtspec->args) != EXPECTED_ARGC)
    {
      errorMsg ("FAILED %s%c: length(tgtspec->args) = %d, expected %d",
                testname, subtest, cdsl_dlist_size (tgtspec->args),
                EXPECTED_ARGC);
      goto cleanup;
    }
  subtest++;

  run2_create_argv_from_dlist (tgtspec->args, NULL, &argc, &argv);
  if (check_args (testname, &subtest, EXPECTED_ARGC, EXPECTED_ARGV, argc, argv) != 0)
    goto cleanup;

  cmd = run2_create_cmdline_from_argv (ustr_cstr (tgtspec->filename), argc, argv);
  if (cmd == NULL)
    {
      errorMsg ("FAILED %s%c: computed command line is NULL",
                testname, subtest);
      goto cleanup;
    }
  subtest++;

  p = strstr (cmd, "MARKER");
  if (!p)
    {
      errorMsg ("FAILED %s%c: computed command line does not contain MARKER |%s|",
                testname, subtest, cmd);
      goto cleanup;
    }
  subtest++;

  if (strcmp (p, EXPECTED_CMDLINE) != 0)
    {
      errorMsg ("FAILED %s%c: cmd args = |%s|, expected |%s|",
                testname, subtest, p, EXPECTED_CMDLINE);
      goto cleanup;
    }
  subtest++;
  infoMsg ("  cmd=|%s|", cmd);
  free (cmd);


  cmd = run2_create_cmdline_from_tgtspec (tgtspec);
  if (cmd == NULL)
    {
      errorMsg ("FAILED %s%c: computed command line is NULL",
                testname, subtest);
      goto cleanup;
    }
  subtest++;
  p = strstr (cmd, "MARKER");
  if (!p)
    {
      errorMsg ("FAILED %s%c: computed command line does not contain MARKER |%s|",
                testname, subtest, cmd);
      goto cleanup;
    }
  subtest++;

  if (strcmp (p, EXPECTED_CMDLINE) != 0)
    {
      errorMsg ("FAILED %s%c: cmd args = |%s|, expected |%s|",
                testname, subtest, p, EXPECTED_CMDLINE);
      goto cleanup;
    }
  subtest++;
  infoMsg ("  cmd=|%s|", cmd);

  rc = 0;

cleanup:
  run2_tgt_spec_delete (tgtspec);
  run2_freeargv (argv);
  if (cmd)
    free (cmd);
  return rc;
}

int test4 (void)
{
  int rc = 1;
  run2_tgt_spec_t *tgtspec;
  const char *testname = "test 4";
  char subtest = 'a';
  char  *startin = NULL;

  tgtspec = run2_tgt_spec_create ();
  run2_tgt_spec_set_startin (tgtspec, USTR1 (\x8, "/usr/bin"));

  run2_tgt_spec_print (stdout, 0, USTR1 (\xF, "test4 (tgtspec)"), tgtspec);

  if (tgtspec->startin == NULL)
    {
      errorMsg ("FAILED %s%c: tgtspec->startin is NULL", testname, subtest);
      goto cleanup;
    }
  subtest++;

  startin = run2_get_startin_directory (tgtspec);
  if (startin == NULL)
    {
      errorMsg ("FAILED %s%c: startin directory is NULL",
                testname, subtest);
      goto cleanup;
    }
  subtest++;

  infoMsg ("  startin=|%s|", startin);
  rc = 0;

cleanup:
  run2_tgt_spec_delete (tgtspec);
  if (startin)
    free (startin);
  return rc;
}

int main (int argc, char * argv[])
{
  int rc = 0;
  run2_set_program_name (argv[0]);
  run2_set_verbose_level (RUN2_DEFAULT_LOG_LEVEL);
  rc += test1 ();
  rc += test2 ();
  rc += test3 ();
  rc += test4 ();
  return rc;
}

